/*
 * 
* Copyright (C) 2004-2009  Autodesk, Inc.
* 
* This library is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser
* General Public License as published by the Free Software Foundation.
* 
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
* 
 */

#include "stdafx.h"
#include "MapInfoConnection.h"
#include "MapInfoFeatureSchema.h"
#include "MapInfoClassDefinition.h"
#include <FdoCommonSchemaUtil.h>
#include <FdoCommonMiscUtil.h>

MapInfoFeatureSchema::MapInfoFeatureSchema(void)
{
    throw FdoException::Create(NlsMsgGetMain(FDO_NLSID(MAPINFOPROVIDER_UNSPECIFIED_ERROR)));
}

MapInfoFeatureSchema::MapInfoFeatureSchema(
    MapInfoConnection* conn,
    FdoFeatureSchema* fdoSchema,
    MapInfoClassDefinitionCollection* mapinfoClasses):
    m_connection(conn),
    m_fdoSchema(fdoSchema),
    m_mapinfoClasses(mapinfoClasses)
{
    VALIDATE_ARGUMENT(conn);
    VALIDATE_ARGUMENT(fdoSchema);
    VALIDATE_ARGUMENT(mapinfoClasses);
    FDO_SAFE_ADDREF(m_connection.p);
    FDO_SAFE_ADDREF(m_fdoSchema.p);
    FDO_SAFE_ADDREF(m_mapinfoClasses.p);

    for (FdoInt32 i = 0; i < mapinfoClasses->GetCount(); ++i)
    {
        FdoPtr<MapInfoClassDefinition> mapinfoClassDef = mapinfoClasses->GetItem(i);
        mapinfoClassDef->SetParent(this);
    }
}


MapInfoFeatureSchema::MapInfoFeatureSchema(
    MapInfoConnection* conn,
    FdoFeatureSchema* fdoSchema):
    m_connection(conn),
    m_fdoSchema(fdoSchema)
{
    VALIDATE_ARGUMENT(conn);
    VALIDATE_ARGUMENT(fdoSchema);
    FDO_SAFE_ADDREF(m_connection.p);
    FDO_SAFE_ADDREF(m_fdoSchema.p);

    FdoPtr<FdoClassCollection> fdoClasses = fdoSchema->GetClasses();
    m_mapinfoClasses = new MapInfoClassDefinitionCollection();
    for (FdoInt32 i = 0; i < fdoClasses->GetCount(); ++i)
    {
        FdoPtr<FdoClassDefinition> fdoClassDef = fdoClasses->GetItem(i);
        FdoPtr<MapInfoClassDefinition> mapinfoClassDef = MapInfoClassDefinition::CreateMapInfoClassDefinition(conn, fdoClassDef);
        m_mapinfoClasses->Add(mapinfoClassDef);
        mapinfoClassDef->SetParent(this);
    }
}

MapInfoFeatureSchema::~MapInfoFeatureSchema(void)
{
}

FdoString * MapInfoFeatureSchema::GetName() const
{
    return m_fdoSchema->GetName();
}

bool MapInfoFeatureSchema::CanSetName() const
{
    return false;
}

FdoString* MapInfoFeatureSchema::GetLocation()
{
    return m_connection->GetDirectory();
}

FdoFeatureSchema * MapInfoFeatureSchema::GetFdoSchema() const
{
    FDO_SAFE_ADDREF(m_fdoSchema.p);
    return m_fdoSchema;
}

MapInfoClassDefinitionCollection* MapInfoFeatureSchema::GetMapInfoClasses()
{
    FDO_SAFE_ADDREF(m_mapinfoClasses.p);
    return m_mapinfoClasses;
}

void MapInfoFeatureSchema::AddClass (IMapInfoFile* layer, FdoClassDefinition* fdoClassDef)
{
    if (m_connection->IsConnectToFile())
        throw FdoCommandException::Create (NlsMsgGetMain(FDO_NLSID(MAPINFOPROVIDER_CANNOT_ADD_CLASS)));

    VALIDATE_ARGUMENT(layer);
    VALIDATE_ARGUMENT(fdoClassDef);

    // Verify the class doesn't already exist:
    FdoPtr<MapInfoClassDefinition> oldMapInfoClass = m_mapinfoClasses->FindItem(fdoClassDef->GetName());
    if (oldMapInfoClass != NULL)
        throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(FDO_126_CLASS_ALREADY_EXISTS), fdoClassDef->GetName()));

    FdoPtr<FdoClassDefinition> newFdoClassDef = FdoCommonSchemaUtil::DeepCopyFdoClassDefinition(fdoClassDef, NULL);
    FdoPtr<MapInfoClassDefinition> newMapInfoClass = new MapInfoClassDefinition(m_connection, layer, newFdoClassDef);
    m_mapinfoClasses->Add(newMapInfoClass);
    newMapInfoClass->SetParent(this);

    FdoPtr<FdoClassCollection> fdoClasses = m_fdoSchema->GetClasses();
    fdoClasses->Add(newFdoClassDef);
}

void MapInfoFeatureSchema::AddClass(FdoClassDefinition* fdoClassDef)
{
    if (m_connection->IsConnectToFile())
        throw FdoCommandException::Create (NlsMsgGetMain(FDO_NLSID(MAPINFOPROVIDER_CANNOT_ADD_CLASS)));

    VALIDATE_ARGUMENT(fdoClassDef);
    FdoString * name = fdoClassDef->GetName();
    if(!name || !name[0])
        throw FdoException::Create(NlsMsgGetMain(MAPINFOPROVIDER_CLASS_NAME_INVALID,"The class name '%1$ls' is invalid.", fdoClassDef->GetName()));

    FdoPtr<MapInfoClassDefinition> mapinfoClassDef = MapInfoClassDefinition::CreateMapInfoClassDefinition(m_connection, fdoClassDef);
    m_mapinfoClasses->Add(mapinfoClassDef);
    mapinfoClassDef->SetParent(this);

    FdoPtr<FdoClassDefinition> newFdoClassDef = mapinfoClassDef->GetFdoClass();
    FdoPtr<FdoClassCollection> fdoClasses = m_fdoSchema->GetClasses();
    fdoClasses->Add(newFdoClassDef);
    m_fdoSchema->AcceptChanges();
}

void MapInfoFeatureSchema::ModifyClass(FdoClassDefinition* fdoClassDef)
{
    VALIDATE_ARGUMENT(fdoClassDef);

    // Determine if the class contains any data:
    FdoPtr<FdoISelect> select = (FdoISelect*)m_connection->CreateCommand(FdoCommandType_Select);
    select->SetFeatureClassName(fdoClassDef->GetQualifiedName());
    FdoPtr<FdoIFeatureReader> reader = select->Execute();
    bool bClassHasData = reader->ReadNext();
    reader->Close();

    // If the class contains data, throw an exception; otherwise, delete/recreate the class:
    if (bClassHasData)
        throw FdoException::Create(NlsMsgGetMain(MAPINFOPROVIDER_MODIFY_CLASS, "Can't modify class '%1$ls' because it contains data.", (FdoString *) fdoClassDef->GetQualifiedName() ));

    FdoString* className = fdoClassDef->GetName();
    FdoPtr<FdoClassCollection> fdoClasses = m_fdoSchema->GetClasses();
    FdoInt32 nIndex = fdoClasses->IndexOf(className);
    if (nIndex < 0)
        throw FdoCommandException::Create(NlsMsgGetMain(MAPINFOPROVIDER_CLASS_NOT_EXIST, "Class '%1$ls' doesn't exist.", className));

    FdoPtr<MapInfoClassDefinition> mapinfoClassDef = m_mapinfoClasses->GetItem(className);
    if (NULL == mapinfoClassDef.p)
        throw FdoCommandException::Create(NlsMsgGetMain(MAPINFOPROVIDER_CLASS_NOT_EXIST, "Class '%1$ls' doesn't exist.", className));

    mapinfoClassDef->ModifyMapInfoFile(fdoClassDef);
    FdoPtr<FdoClassDefinition> newFdoClassDef = mapinfoClassDef->GetFdoClass();
    fdoClasses->RemoveAt(nIndex);
    fdoClasses->Add(newFdoClassDef);
    m_fdoSchema->AcceptChanges();
}

void MapInfoFeatureSchema::DeleteClass (MapInfoClassDefinition* mapinfoClassDef)
{
    VALIDATE_ARGUMENT(mapinfoClassDef);

    if (!m_mapinfoClasses->Contains(mapinfoClassDef))
        throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(MAPINFOPROVIDER_CLASS_NOT_EXIST), mapinfoClassDef->GetName()));

    mapinfoClassDef->DeleteMapInfoFile();
    m_mapinfoClasses->Remove(mapinfoClassDef);

    FdoPtr<FdoClassCollection> fdoClasses = m_fdoSchema->GetClasses();
    FdoPtr<FdoClassDefinition> fdoClassDef = fdoClasses->FindItem(mapinfoClassDef->GetName());
    fdoClasses->Remove(fdoClassDef);
    m_fdoSchema->AcceptChanges();
}

void MapInfoFeatureSchema::DeleteAllClasses()
{
    for (int i = 0; i < m_mapinfoClasses->GetCount(); ++i)
    {
        FdoPtr<MapInfoClassDefinition> mapinfoClass = m_mapinfoClasses->GetItem(i);
        mapinfoClass->DeleteMapInfoFile();
    }
    m_mapinfoClasses->Clear();

    FdoPtr<FdoClassCollection> fdoClasses = m_fdoSchema->GetClasses();
    fdoClasses->Clear();
    m_fdoSchema->AcceptChanges();
}

///////////////////////////////////////////////////////////////////////////////


MapInfoFeatureSchemaCollection::MapInfoFeatureSchemaCollection(void)
{
}

FdoFeatureSchemaCollection* MapInfoFeatureSchemaCollection::GetFdoSchemas()
{
    FdoPtr<FdoFeatureSchemaCollection> fdoSchemas = FdoFeatureSchemaCollection::Create (NULL);
    for (FdoInt32 i = 0; i < GetCount(); ++i)
    {
        FdoPtr<MapInfoFeatureSchema> mapinfoSchema = GetItem(i);
        FdoPtr<FdoFeatureSchema> fdoSchema = mapinfoSchema->GetFdoSchema();
        FdoPtr<FdoFeatureSchema> newFdoSchema = FdoCommonSchemaUtil::DeepCopyFdoFeatureSchema(fdoSchema, NULL);
        fdoSchemas->Add(newFdoSchema);
        newFdoSchema->AcceptChanges();
    }
    return fdoSchemas.Detach();
}

FdoInt32 MapInfoFeatureSchemaCollection::Add( MapInfoFeatureSchema* value)
{
    FdoPtr<MapInfoFeatureSchema> schema = FindItem (value->GetName ());
    if (schema != NULL)
    {
        FdoPtr<MapInfoClassDefinitionCollection> new_classes = value->GetMapInfoClasses ();
        FdoPtr<MapInfoClassDefinitionCollection> existing_classes = schema->GetMapInfoClasses ();
        FdoPtr<FdoFeatureSchema> existing_logical_schema = schema->GetFdoSchema ();
        FdoPtr<FdoClassCollection> existing_logical_classes = existing_logical_schema->GetClasses ();
        FdoPtr<FdoFeatureSchema> new_logical_schema = value->GetFdoSchema ();
        FdoPtr<FdoClassCollection> new_logical_classes = new_logical_schema->GetClasses ();
        for (int i = 0; i < new_classes->GetCount (); i++)
        {
            FdoPtr<MapInfoClassDefinition> definition = new_classes->GetItem (i);
            definition->SetParent(schema);
            existing_classes->Add (definition);
            FdoPtr<FdoClassDefinition> cls = new_logical_classes->GetItem (definition->GetName ());
            new_logical_classes->Remove (cls);
            existing_logical_classes->Add (cls);
        }
    }
    else
        return FdoNamedCollection<MapInfoFeatureSchema, FdoException>::Add(value);

    return IndexOf(value);
}
